#include "vzconn/conn/vzclient.h"
#include "vzconn/base/helper.h"
#include "vzconn/conn/ipcconnect.h"

namespace vzconn {

////////////////////////////////////////////////////////////////////////////////

VzConnect::Ptr VzClient::ConnectTcpServer(
  boost::asio::io_service& io_service,
  const std::string addr,
  uint16 port,
  CallBackConnect call_back) {
  LOG(INFO) << "Connect Server : " << addr << ":" << port;
  TcpConnect *conn = new TcpConnect(io_service);
  VzConnect::Ptr vz_connect(conn);
  boost::asio::ip::tcp::endpoint server_addr(
    boost::asio::ip::address().from_string(addr), port);

  conn->socket().async_connect(
    server_addr,
    boost::bind(&VzClient::HandleStaticServerConnect,
                vz_connect,
                boost::asio::placeholders::error,
                call_back));
  return vz_connect;
}

VzConnect::Ptr VzClient::ConnectIpcServer(
  boost::asio::io_service& io_service,
  const std::string addr,
  uint16 port,
  CallBackConnect call_back) {

#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
  IpcConnect *conn = new IpcConnect(io_service);
  LOG(INFO) << "Connect Server : " << NetAddrToIpcAddr(addr, port);
  boost::asio::local::stream_protocol::endpoint server_addr(
    NetAddrToIpcAddr(addr, port));
#else
  port ++;
  LOG(INFO) << "Not support IPC, Convert Tcp Connect";
  LOG(INFO) << "Connect Server : " << addr << ":" << port;
  TcpConnect *conn = new TcpConnect(io_service);
  boost::asio::ip::tcp::endpoint server_addr(
    boost::asio::ip::address().from_string(addr), port);
#endif
  VzConnect::Ptr vz_connect(conn);

  conn->socket().async_connect(
    server_addr,
    boost::bind(&VzClient::HandleStaticServerConnect,
                vz_connect,
                boost::asio::placeholders::error,
                call_back));
  return vz_connect;
}

void VzClient::HandleStaticServerConnect(
  VzConnect::Ptr connect,
  const boost::system::error_code& err,
  CallBackConnect call_back) {
  // LOG(INFO) << "HandleStaticServerConnect";
  if (err) {
    call_back(connect, err);
  } else {
    // LOG(INFO) << "Start vzconn";
    if(!connect->Start()) {
      LOG(INFO) << "Failure to start vzconnect";
    }
    call_back(connect, err);
  }
}

////////////////////////////////////////////////////////////////////////////////
BlockVzConnect::Ptr VzClient::ConnectBlockTcpServer(
  boost::asio::io_service& io_service,
  const std::string addr,
  uint16 port) {

  BlockVzTcpConnect *conn = new BlockVzTcpConnect(io_service);
  try {
    boost::asio::ip::tcp::endpoint server_addr(
      boost::asio::ip::address().from_string(addr), port);
    conn->socket_.connect(server_addr);
  } catch (std::exception &e) {
    LOG(ERROR) << e.what();
    return BlockVzConnect::Ptr();
  }

  return BlockVzConnect::Ptr(conn);
}

BlockVzConnect::Ptr VzClient::ConnectBlockIpcServer(
  boost::asio::io_service& io_service,
  const std::string addr,
  uint16 port) {

#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
  BlockVzIpcConnect *conn = new BlockVzIpcConnect(io_service);
  LOG(INFO) << "Connect Server : " << NetAddrToIpcAddr(addr, port);
  boost::asio::local::stream_protocol::endpoint server_addr(
    NetAddrToIpcAddr(addr, port));
#else
  port++;
  LOG(INFO) << "Not support IPC, Convert Tcp Connect";
  LOG(INFO) << "Connect Server : " << addr << ":" << port;
  BlockVzTcpConnect *conn = new BlockVzTcpConnect(io_service);
  boost::asio::ip::tcp::endpoint server_addr(
    boost::asio::ip::address().from_string(addr), port);
#endif
  try {
    conn->socket_.connect(server_addr);
  } catch (std::exception &e) {
    LOG(ERROR) << e.what();
    return BlockVzConnect::Ptr();
  }
  return BlockVzConnect::Ptr(conn);
}

}